package order

import (
	"context"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/service/backend/common/utils/pointy"
	"mall/service/order/model"
	"mall/service/order/rpc/internal/svc"
	"mall/service/order/rpc/types/order"

	"github.com/zeromicro/go-zero/core/logx"
)

type GetOrderBySnLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetOrderBySnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetOrderBySnLogic {
	return &GetOrderBySnLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetOrderBySnLogic) GetOrderBySn(in *order.GetOrderBySnReq) (*order.OrderInfo, error) {
	var trade *model.Order
	var payment *model.OrderPayment
	var refund *model.OrderRefund
	if in.Sn != nil && len(strings.TrimSpace(*in.Sn)) > 0 {
		sn := strings.TrimSpace(*in.Sn)
		filterMap := make(map[string]map[string][]any)
		filterMap[model.OrderColumns.OrderSN] = map[string][]any{
			"equalTo": []any{sn},
		}
		list, _, err := l.svcCtx.Order.GetList(l.ctx, model.M{
			"pageSize": uint64(1),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(list) == 0 {
			return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
		}

		trade = list[0]
	} else if in.ShipmentNo != nil && len(strings.TrimSpace(*in.ShipmentNo)) > 0 {
		sn := strings.TrimSpace(*in.ShipmentNo)
		filterMap := make(map[string]map[string][]any)
		filterMap[model.OrderShipmentColumns.No] = map[string][]any{
			"equalTo": []any{sn},
		}
		list, _, err := l.svcCtx.Order.GetShipmentList(l.ctx, model.M{
			"pageSize": uint64(1),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(list) == 0 {
			return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
		}

		shipment := list[0]
		trade, err = l.svcCtx.Order.FindOne(l.ctx, shipment.OrderID)
		if err != nil {
			if err == model.ErrNotFound {
				return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
			}

			return nil, err
		}
	} else if in.RefundNo != nil && len(strings.TrimSpace(*in.RefundNo)) > 0 {
		no := strings.TrimSpace(*in.RefundNo)
		filterMap := make(map[string]map[string][]any)
		filterMap[model.OrderRefundColumns.RefundNo] = map[string][]any{
			"equalTo": []any{no},
		}
		list, _, err := l.svcCtx.Order.GetRefundList(l.ctx, model.M{
			"pageSize": uint64(1),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(list) == 0 {
			return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
		}

		refund = list[0]

		paymentFilterMap := make(map[string]map[string][]any)
		paymentFilterMap[model.OrderPaymentColumns.OrderPaymentID] = map[string][]any{
			"equalTo": []any{refund.OrderPaymentID},
		}
		paymentList, _, err := l.svcCtx.Order.GetPaymentList(l.ctx, model.M{
			"pageSize": uint64(1),
		}, -1, paymentFilterMap)
		if err != nil {
			return nil, err
		}

		if len(paymentList) == 0 {
			return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
		}

		payment = paymentList[0]
		if in.PaymentNo != nil && len(strings.TrimSpace(*in.PaymentNo)) > 0 {
			if payment.No != strings.TrimSpace(*in.PaymentNo) {
				return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
			}
		}

		trade, err = l.svcCtx.Order.FindOne(l.ctx, refund.OrderID)
		if err != nil {
			if err == model.ErrNotFound {
				return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
			}

			return nil, err
		}
	} else if in.PaymentNo != nil && len(strings.TrimSpace(*in.PaymentNo)) > 0 {
		sn := strings.TrimSpace(*in.PaymentNo)
		filterMap := make(map[string]map[string][]any)
		filterMap[model.OrderPaymentColumns.No] = map[string][]any{
			"equalTo": []any{sn},
		}
		list, _, err := l.svcCtx.Order.GetPaymentList(l.ctx, model.M{
			"pageSize": uint64(1),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(list) == 0 {
			return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
		}

		payment = list[0]
		trade, err = l.svcCtx.Order.FindOne(l.ctx, payment.OrderID)
		if err != nil {
			if err == model.ErrNotFound {
				return nil, resultx.StatusError(resultx.ORDER_PARAM_INVALID, "")
			}

			return nil, err
		}
	}

	var skus []*order.OrderSkuInfo
	if in.WithSku != nil && *in.WithSku > 0 {
		skuFilterMap := make(map[string]map[string][]any)
		skuFilterMap[model.OrderItemColumns.OrderID] = map[string][]any{
			"equalTo": []any{trade.ID},
		}
		itemList, _, err := l.svcCtx.Order.GetItemList(l.ctx, model.M{}, -1, skuFilterMap)
		if err != nil {
			return nil, err
		}

		for _, v := range itemList {
			skus = append(skus, &order.OrderSkuInfo{
				Id:             &v.OrderItemID,
				OrderId:        &v.OrderID,
				ProductCode:    &v.ProductCode,
				SkuId:          &v.SkuID,
				SkuCode:        &v.SkuCode,
				Sku:            &v.Sku,
				ImageId:        pointy.GetPointer(strconv.FormatInt(int64(v.SkuImageID), 10)),
				ImageUrl:       &v.SkuImageURL,
				ProductId:      &v.ProductID,
				EntryId:        &v.EntryID,
				Title:          &v.Title,
				Slug:           &v.Slug,
				ProductType:    pointy.GetPointer(uint32(v.ProductType)),
				BrandId:        &v.BrandID,
				BrandTitle:     &v.BrandTitle,
				BrandSlug:      &v.BrandSlug,
				GemPrice:       pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice))),
				RetailPrice:    pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailPrice))),
				Quantity:       pointy.GetPointer(uint32(v.Quantity)),
				Weight:         pointy.GetPointer(moneyx.FormatWeight(int64(v.Weight))),
				TradeState:     pointy.GetPointer(uint32(v.TradeState)),
				Status:         pointy.GetPointer(uint32(v.Status)),
				GemAmount:      pointy.GetPointer(moneyx.FormatGem(int64(v.GemAmount))),
				RetailAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailAmount))),
				DiscountAmount: pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
				AdjustAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.AdjustAmount))),
				ActualAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.ActualAmount))),
				RefundAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.RefundAmount))),
				RefundQuantity: pointy.GetPointer(uint32(v.RefundQuantity)),
			})
		}
	}

	var promotions []*order.OrderDiscountInfo
	if in.WithPromotion != nil && *in.WithPromotion > 0 {
		promotionFilterMap := make(map[string]map[string][]any)
		promotionFilterMap[model.OrderDiscountColumns.OrderID] = map[string][]any{
			"equalTo": []any{trade.ID},
		}
		promotionList, _, err := l.svcCtx.Order.GetDiscountList(l.ctx, model.M{}, -1, promotionFilterMap)
		if err != nil {
			return nil, err
		}

		if len(promotionList) > 0 {
			for _, v := range promotionList {
				promotions = append(promotions, &order.OrderDiscountInfo{
					Id:               &v.OrderDiscountID,
					OrderId:          &v.OrderID,
					SkuId:            &v.SkuID,
					ProductId:        &v.ProductID,
					Title:            &v.Title,
					Slug:             &v.Slug,
					ActivityId:       &v.ActivityID,
					ActivityRuleId:   &v.ActivityRuleID,
					CouponTemplateId: &v.CouponTemplateID,
					CouponId:         &v.CouponID,
					GrouponAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.GrouponAmount))),
					WholesaleAmount:  pointy.GetPointer(moneyx.FormatAmount(int64(v.WholesaleAmount))),
					CouponAmount:     pointy.GetPointer(moneyx.FormatAmount(int64(v.CouponAmount))),
					ActivityAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.ActivityAmount))),
					IntegralAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.IntegralAmount))),
					DiscountAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
					Integrals:        &v.Integrals,
				})
			}
		}
	}

	var payments []*order.OrderPaymentInfo
	if in.WithPayment != nil && *in.WithPayment > 0 {
		if payment != nil {
			payments = append(payments, &order.OrderPaymentInfo{
				Id:            pointy.GetPointer(payment.OrderPaymentID),
				OrderId:       pointy.GetPointer(payment.OrderID),
				No:            pointy.GetPointer(payment.No),
				TransactionID: pointy.GetPointer(payment.TransactionID),
				Amount:        pointy.GetPointer(moneyx.FormatAmount(int64(payment.Amount))),
				Type:          pointy.GetPointer(uint32(payment.Type)),
				Name:          pointy.GetPointer(payment.Name),
				Code:          pointy.GetPointer(payment.Code),
				Status:        pointy.GetPointer(uint32(payment.Status)),
				PaidAt:        pointy.GetPointer(modelx.FormatNullDotTime(payment.PaidAt, "")),
			})
		} else {
			paymentFilterMap := make(map[string]map[string][]any)
			paymentFilterMap[model.OrderPaymentColumns.OrderID] = map[string][]any{
				"equalTo": []any{trade.ID},
			}
			paymentFilterMap[model.OrderPaymentColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.PaymentStatusNotPay},
			}
			paymentList, _, err := l.svcCtx.Order.GetPaymentList(l.ctx, model.M{
				"pageSize": uint64(1),
				"orderBy":  model.OrderPaymentColumns.OrderPaymentID + " DESC",
			}, -1, paymentFilterMap)
			if err != nil {
				return nil, err
			}

			if len(paymentList) > 0 {
				for _, v := range paymentList {
					payments = append(payments, &order.OrderPaymentInfo{
						Id:      &v.OrderPaymentID,
						OrderId: &v.OrderID,
						No:      &v.No,
						Amount:  pointy.GetPointer(moneyx.FormatAmount(int64(v.Amount))),
						Type:    pointy.GetPointer(uint32(v.Type)),
						Name:    &v.Name,
						Code:    &v.Code,
						Status:  pointy.GetPointer(uint32(v.Status)),
						PaidAt:  pointy.GetPointer(modelx.FormatNullDotTime(v.PaidAt, "")),
					})
				}
			}
		}
	}

	var refunds []*order.OrderRefundInfo
	if in.WithRefund != nil && *in.WithRefund > 0 {
		if refund != nil {
			refunds = append(refunds, &order.OrderRefundInfo{
				Id:       pointy.GetPointer(refund.OrderRefundID),
				OrderId:  pointy.GetPointer(refund.OrderID),
				RefundNo: pointy.GetPointer(refund.RefundNo),
				RefundId: pointy.GetPointer(refund.RefundID),
				Amount:   pointy.GetPointer(moneyx.FormatAmount(int64(refund.Amount))),
				Status:   pointy.GetPointer(uint32(refund.Status)),
				RefundAt: pointy.GetPointer(modelx.FormatNullDotTime(refund.RefundAt, "")),
			})
		} else {
			refundFilterMap := make(map[string]map[string][]any)
			refundFilterMap[model.OrderRefundColumns.OrderID] = map[string][]any{
				"equalTo": []any{trade.ID},
			}
			refundFilterMap[model.OrderRefundColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.PaymentStatusUserClosed},
			}
			refundList, _, err := l.svcCtx.Order.GetRefundList(l.ctx, model.M{
				"pageSize": uint64(1),
				"orderBy":  model.OrderRefundColumns.OrderRefundID + " DESC",
			}, -1, refundFilterMap)
			if err != nil {
				return nil, err
			}

			if len(refundList) > 0 {
				for _, v := range refundList {
					refunds = append(refunds, &order.OrderRefundInfo{
						Id:       pointy.GetPointer(v.OrderRefundID),
						OrderId:  pointy.GetPointer(v.OrderID),
						RefundNo: pointy.GetPointer(v.RefundNo),
						RefundId: pointy.GetPointer(v.RefundID),
						Amount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.Amount))),
						Status:   pointy.GetPointer(uint32(v.Status)),
						RefundAt: pointy.GetPointer(modelx.FormatNullDotTime(v.RefundAt, "")),
					})
				}
			}
		}
	}

	return &order.OrderInfo{
		Id:              &trade.ID,
		ParentId:        &trade.ParentID,
		UserId:          &trade.UserID,
		UserRank:        &trade.UserRank,
		Buyer:           &trade.Buyer,
		Sn:              &trade.OrderSN,
		OrderType:       pointy.GetPointer(uint32(trade.OrderType)),
		SourceType:      pointy.GetPointer(uint32(trade.SourceType)),
		Number:          pointy.GetPointer(uint32(trade.Quantity)),
		GemAmount:       pointy.GetPointer(moneyx.FormatGem(int64(trade.GemAmount))),
		RetailAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.RetailAmount))),
		Freight:         pointy.GetPointer(moneyx.FormatAmount(int64(trade.Freight))),
		DiscountAmount:  pointy.GetPointer(moneyx.FormatAmount(int64(trade.DiscountAmount))),
		AdjustAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.AdjustAmount))),
		ActualAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.ActualAmount))),
		DepositAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(trade.DepositAmount))),
		BalanceAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(trade.BalanceAmount))),
		RemainingAmount: pointy.GetPointer(moneyx.FormatAmount(int64(trade.RemainingAmount))),
		RefundAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.RefundAmount))),
		Gems:            &trade.Gems,
		TradeState:      pointy.GetPointer(uint32(trade.TradeState)),
		Remark:          &trade.Remark,
		SellerMemo:      &trade.SellerMemo,
		CreatedAt:       pointy.GetPointer(modelx.FormatTime(trade.CreatedAt, "")),
		UpdatedAt:       pointy.GetPointer(modelx.FormatTime(trade.UpdatedAt, "")),
		DeletedAt:       pointy.GetPointer(modelx.FormatNullDotTime(trade.DeletedAt, "")),
		PaidAt:          pointy.GetPointer(modelx.FormatNullDotTime(trade.PaidAt, "")),
		ShippedAt:       pointy.GetPointer(modelx.FormatNullDotTime(trade.ShippedAt, "")),
		DeliveredAt:     pointy.GetPointer(modelx.FormatNullDotTime(trade.DeliveredAt, "")),
		ConfirmedAt:     pointy.GetPointer(modelx.FormatNullDotTime(trade.ConfirmedAt, "")),
		ReviewedAt:      pointy.GetPointer(modelx.FormatNullDotTime(trade.ReviewedAt, "")),
		FulfilledAt:     pointy.GetPointer(modelx.FormatNullDotTime(trade.FulfilledAt, "")),
		ClosedAt:        pointy.GetPointer(modelx.FormatNullDotTime(trade.ClosedAt, "")),
		AutoComfirmDays: pointy.GetPointer(uint32(trade.AutoComfirmDays)),
		Growth:          pointy.GetPointer(uint32(trade.Growth)),
		Integrals:       pointy.GetPointer(uint32(trade.Integrals)),
		Skus:            skus,
		Promotions:      promotions,
		Payments:        payments,
		Refunds:         refunds,
	}, nil
}
